home *** CD-ROM | disk | FTP | other *** search
/ MacFormat 1995 March / macformat-022.iso / Shareware City / Developers / GNU Diff Sources / GNU DIFF 1.15b Sources / io.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-04-13  |  18.8 KB  |  688 lines  |  [TEXT/ALFA]

  1. /* File I/O for GNU DIFF.
  2.    Copyright (C) 1988, 1989 Free Software Foundation, Inc.
  3.  
  4. This file is part of GNU DIFF.
  5.  
  6. GNU DIFF is free software; you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation; either version 1, or (at your option)
  9. any later version.
  10.  
  11. GNU DIFF is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. GNU General Public License for more details.
  15.  
  16. You should have received a copy of the GNU General Public License
  17. along with GNU DIFF; see the file COPYING.  If not, write to
  18. the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
  19.  
  20. #include "diff.h"
  21.  
  22. /* Rotate a value n bits to the left. */
  23. #define UINT_BIT (sizeof (unsigned) * CHAR_BIT)
  24. #define ROL(v, n) ((v) << (n) | (v) >> UINT_BIT - (n))
  25.  
  26. /* Given a hash value and a new character, return a new hash value. */
  27. #define HASH(h, c) ((c) + ROL (h, 7))
  28.  
  29. #ifndef    EXCLUDE_MAIN
  30. /* Check for binary files and compare them for exact identity.  */
  31.  
  32. /* Return 1 if BUF contains a non text character.
  33.    SIZE is the number of characters in BUF.  */
  34.  
  35. static int
  36. binary_file_p (buf, size)
  37.      char *buf;
  38.      LONG size;
  39. {
  40.   static const char textchar[] = {
  41.     /* ISO 8859 */
  42.     0, 0, 0, 0, 0, 0, 0, 0,
  43.     0, 1, 1, 1, 1, 1, 0, 0,
  44.     0, 0, 0, 0, 0, 0, 0, 0,
  45.     0, 0, 0, 0, 0, 0, 0, 0,
  46.     1, 1, 1, 1, 1, 1, 1, 1,
  47.     1, 1, 1, 1, 1, 1, 1, 1,
  48.     1, 1, 1, 1, 1, 1, 1, 1,
  49.     1, 1, 1, 1, 1, 1, 1, 1,
  50.     1, 1, 1, 1, 1, 1, 1, 1,
  51.     1, 1, 1, 1, 1, 1, 1, 1,
  52.     1, 1, 1, 1, 1, 1, 1, 1,
  53.     1, 1, 1, 1, 1, 1, 1, 1,
  54.     1, 1, 1, 1, 1, 1, 1, 1,
  55.     1, 1, 1, 1, 1, 1, 1, 1,
  56.     1, 1, 1, 1, 1, 1, 1, 1,
  57.     1, 1, 1, 1, 1, 1, 1, 0,
  58.     0, 0, 0, 0, 0, 0, 0, 0,
  59.     0, 0, 0, 0, 0, 0, 0, 0,
  60.     0, 0, 0, 0, 0, 0, 0, 0,
  61.     0, 0, 0, 0, 0, 0, 0, 0,
  62.     1, 1, 1, 1, 1, 1, 1, 1,
  63.     1, 1, 1, 1, 1, 1, 1, 1,
  64.     1, 1, 1, 1, 1, 1, 1, 1,
  65.     1, 1, 1, 1, 1, 1, 1, 1,
  66.     1, 1, 1, 1, 1, 1, 1, 1,
  67.     1, 1, 1, 1, 1, 1, 1, 1,
  68.     1, 1, 1, 1, 1, 1, 1, 1,
  69.     1, 1, 1, 1, 1, 1, 1, 1,
  70.     1, 1, 1, 1, 1, 1, 1, 1,
  71.     1, 1, 1, 1, 1, 1, 1, 1,
  72.     1, 1, 1, 1, 1, 1, 1, 1,
  73.     1, 1, 1, 1, 1, 1, 1, 1,
  74.   };
  75.   while (--size >= 0)
  76.     if (!textchar[*buf++ & 0377])
  77.       return 1;
  78.   return 0;
  79.     }
  80.  
  81. LONG binary_file_threshold = 512;
  82.  
  83. /* Slurp the current file completely into core.
  84.    Return nonzero if it appears to be a binary file.  */
  85.  
  86. static int
  87. slurp (struct file_data *current)
  88. {
  89.   /* If we have a nonexistent file at this stage, treat it as empty.  */
  90.   if (current->desc < 0)
  91.     {
  92.       current->bufsize = 0;
  93.       current->buffered_chars = 0;
  94.       current->buffer = 0;
  95.     }
  96.   /* If it's a regular file, we can just get the size out of the stat
  97.      block and slurp it in all at once. */
  98.   /* In all cases, we leave room in the buffer for 2 extra chars
  99.      beyond those that current->bufsize describes:
  100.      one for a newline (in case the text does not end with one)
  101.      and one for a sentinel in find_identical_ends.  */
  102.   else if ((current->stat.st_mode & S_IFMT) == S_IFREG)
  103.     {
  104.         current->bufsize = current->stat.st_size;
  105.         current->buffer = (char *) xmalloc (current->bufsize + 2);
  106. #ifdef    THINK_C
  107.         {
  108.             int    count;
  109.             while((count = read(current->desc,
  110.                 current->buffer+current->buffered_chars,
  111.                     32000)) > 0)
  112.                 current->buffered_chars += count;
  113.         }
  114. #else
  115.       current->buffered_chars
  116.     = read (current->desc, current->buffer, current->bufsize);
  117. #endif    THINK_C
  118.       if (current->buffered_chars < 0)
  119.     pfatal_with_name (current->name);
  120.     }
  121.   else
  122.     {
  123.       int cc;
  124.  
  125.       current->bufsize = 4096;
  126.       current->buffer = (char *) xmalloc (current->bufsize + 2);
  127.       current->buffered_chars = 0;
  128.       
  129.       /* Not a regular file; read it in a little at a time, growing the
  130.      buffer as necessary. */
  131.       while ((cc = read (current->desc,
  132.             current->buffer + current->buffered_chars,
  133.              current->bufsize - current->buffered_chars))
  134.          > 0)
  135.     {
  136.       current->buffered_chars += cc;
  137.       if (current->buffered_chars == current->bufsize)
  138.         {
  139.           current->bufsize = current->bufsize * 2;
  140.           current->buffer = (char *) xrealloc (current->buffer,
  141.                            current->bufsize + 2);
  142.         }
  143.     }
  144.       if (cc < 0)
  145.     pfatal_with_name (current->name);
  146.     }
  147.   
  148.   /* Check first part of file to see if it's a binary file.  */
  149.   if (! always_text_flag
  150.       && binary_file_p (current->buffer,
  151.             min (current->buffered_chars, binary_file_threshold)))
  152.     return 1;
  153.  
  154.   /* If not binary, make sure text ends in a newline,
  155.          but remember that we had to add one unless -B is in effect.  */
  156.     if (current->buffered_chars > 0
  157.         && current->buffer[current->buffered_chars - 1] != NEWLINE)
  158.     {
  159.         current->missing_newline = !ignore_blank_lines_flag;
  160.         current->buffer[current->buffered_chars++] = NEWLINE;
  161.     }
  162.     else
  163.         current->missing_newline = 0;
  164.  
  165.   /* Don't use uninitialized storage. */
  166.   if (current->buffer != 0)
  167.     current->buffer[current->buffered_chars] = '\0';
  168.  
  169.   return 0;
  170. }
  171. #endif    EXCLUDE_MAIN
  172.  
  173. /* Split the file into lines, simultaneously computing the hash codes for
  174.    each line. */
  175.  
  176. diff_err
  177. find_and_hash_each_line (struct file_data *current )
  178. {
  179.     unsigned LONG h;
  180.     LONG i;
  181.     unsigned char *p = (unsigned char *) (current->prefix_end + current->buffer), *ip, c;
  182.  
  183.     if ( ! current->linbuf )
  184.     {
  185.   /* Attempt to get a good initial guess as to the number of lines. */
  186.   current->linbufsize = current->buffered_chars / 50 + 5;
  187.   current->linbuf
  188.     = (struct line_def *) xmalloc (current->linbufsize * sizeof (struct line_def));
  189.   if ( ! current->linbuf )
  190.     return (memory_err);
  191.     }
  192. #ifndef    DIRECT_DIFF
  193.     if (function_regexp || output_style == OUTPUT_IFDEF)
  194. #endif
  195.     {
  196.       /* If the -C, -D or -F option is used, we need to find the lines
  197.      of the matching prefix.  At least we will need to find the last few,
  198.      but since we don't know how many, it's easiest to find them all.
  199.      If -D is specified, we need all the lines of the first file.  */
  200.       current->buffered_lines = 0;
  201.       p = (unsigned char *) current->buffer;
  202.     }
  203. #ifndef    DIRECT_DIFF
  204.   else
  205.     {
  206.       /* Skip the identical prefixes, except be prepared to handle context.
  207.      In fact, handle 1 more preceding line than the context says,
  208.      in case shift_boundaries moves things backwards in this file.  */
  209.         current->buffered_lines = current->prefix_lines - context - 1;
  210.       if (current->buffered_lines < 0)
  211.     current->buffered_lines = 0;
  212.       for (i = 0; i < context + 1; ++i)
  213.     /* Unless we are at the beginning, */
  214.     if ((char *) p != current->buffer)
  215.       /* Back up at least 1 char until at the start of a line.  */
  216.       while ((char *) --p != current->buffer && p[-1] != NEWLINE)
  217.       ;
  218.     }
  219. #endif    DIRECT_DIFF
  220.  
  221.     while ((char *) p < current->suffix_begin + current->buffer)
  222.     {
  223.       h = 0;
  224.       ip = p;
  225.  
  226.         if (current->prefix_end + current->buffer <= (char *) p)
  227.     {
  228.       /* Hash this line until we find a newline. */
  229.       if (ignore_case_flag)
  230.         {
  231.           if (ignore_all_space_flag)
  232.                     while ((c = *p) != NEWLINE)
  233.           {
  234.             if (! isspace (c))
  235.               if (isupper (c))
  236.             h = HASH (h, tolower (c));
  237.               else
  238.             h = HASH (h, c);
  239.             ++p;
  240.           }
  241.           else if (ignore_space_change_flag)
  242.  
  243.                     while ((c = *p) != NEWLINE)
  244.           {
  245.             if (c == ' ' || c == '\t')
  246.               {
  247.             while ((c = *p) == ' ' || c == '\t')
  248.               ++p;
  249.                             if (c == NEWLINE)
  250.               break;
  251.             h = HASH (h, ' ');
  252.               }
  253.             /* C is now the first non-space.  */
  254.             if (isupper (c))
  255.               h = HASH (h, tolower (c));
  256.             else
  257.               h = HASH (h, c);
  258.             ++p;
  259.           }
  260.           else
  261.                     while ((c = *p) != NEWLINE)
  262.           {
  263.             if (isupper (c))
  264.               h = HASH (h, tolower (c));
  265.             else
  266.               h = HASH (h, c);
  267.             ++p;
  268.           }
  269.         }
  270.       else
  271.         {
  272.           if (ignore_all_space_flag)
  273.                     while ((c = *p) != NEWLINE)
  274.           {
  275.             if (! isspace (c))
  276.               h = HASH (h, c);
  277.             ++p;
  278.           }
  279.           else if (ignore_space_change_flag)
  280.                     while ((c = *p) != NEWLINE)
  281.           {
  282.             if (c == ' ' || c == '\t')
  283.               {
  284.             while ((c = *p) == ' ' || c == '\t')
  285.               ++p;
  286.                             if (c == NEWLINE)
  287.               break;
  288.             h = HASH (h, ' ');
  289.               }
  290.             /* C is not the first non-space.  */
  291.               h = HASH (h, c);
  292.             ++p;
  293.           }
  294.           else
  295.                     while ((c = *p) != NEWLINE)
  296.           {
  297.             h = HASH (h, c);
  298.             ++p;
  299.           }
  300.         }
  301.     }
  302.       else
  303.     /* This line is part of the matching prefix,
  304.        so we don't need to hash it.  */
  305.             while (*p != NEWLINE)
  306.       ++p;
  307.       
  308.       /* Maybe increase the size of the line table. */
  309.       if (current->buffered_lines >= current->linbufsize)
  310.     {
  311.       while (current->buffered_lines >= current->linbufsize)
  312.         current->linbufsize *= 2;
  313.       current->linbuf
  314.         = (struct line_def *) xrealloc (current->linbuf,
  315.                         current->linbufsize
  316.                         * sizeof (struct line_def));
  317.       if ( ! current->linbuf )
  318.         return (memory_err);
  319.     }
  320.       current->linbuf[current->buffered_lines].text = (char *) ip;
  321.       current->linbuf[current->buffered_lines].length = p - ip + 1;
  322.       current->linbuf[current->buffered_lines].hash = h;
  323.       ++current->buffered_lines;
  324.       ++p;
  325.     }
  326.  
  327.   i = 0;
  328.   while (
  329. #ifndef    DIRECT_DIFF
  330.     (i < context || output_style == OUTPUT_IFDEF)
  331.      &&
  332. #endif
  333.      (char *) p < current->buffer + current->buffered_chars)
  334.     {
  335.       ip = p;
  336.         while (*p++ != NEWLINE)
  337.     ;
  338.       /* Maybe increase the size of the line table. */
  339.       if (current->buffered_lines >= current->linbufsize)
  340.     {
  341.       while (current->buffered_lines >= current->linbufsize)
  342.         current->linbufsize *= 2;
  343.       current->linbuf
  344.         = (struct line_def *) xrealloc (current->linbuf,
  345.                         current->linbufsize
  346.                         * sizeof (struct line_def));
  347.       if ( ! current->linbuf )
  348.         return (memory_err);
  349.     }
  350.       current->linbuf[current->buffered_lines].text = (char *) ip;
  351.       current->linbuf[current->buffered_lines].length = p - ip;
  352.       current->linbuf[current->buffered_lines].hash = 0;
  353.       ++current->buffered_lines;
  354.       ++i;
  355.     }
  356.  
  357.   if (ROBUST_OUTPUT_STYLE (output_style)
  358.       && current->missing_newline
  359.       && current->suffix_begin == current->buffered_chars)
  360.     --current->linbuf[current->buffered_lines - 1].length;
  361.   return 0;
  362. }
  363.  
  364. /* Given a vector of two file_data objects, find the identical
  365.    prefixes and suffixes of each object. */
  366.  
  367. static void
  368. find_identical_ends (filevec)
  369.     struct file_data filevec[];
  370. {
  371.   char *p0, *p1, *end0, *beg0;
  372.   LONG lines;
  373.  
  374.   if (filevec[0].buffered_chars == 0 || filevec[1].buffered_chars == 0)
  375.     {
  376.       filevec[0].prefix_end = 0;
  377.       filevec[1].prefix_end = 0;
  378.       filevec[0].prefix_lines = filevec[1].prefix_lines = 0;
  379.       filevec[0].suffix_begin = filevec[0].buffered_chars;
  380.       filevec[1].suffix_begin = filevec[1].buffered_chars;
  381.       filevec[0].suffix_lines = filevec[1].suffix_lines = 0;
  382.       return;
  383.     }
  384.  
  385.   /* Find identical prefix.  */
  386.  
  387.     p0 = filevec[0].buffer;
  388.     p1 = filevec[1].buffer;
  389.   lines = 0;
  390.  
  391.   /* Insert end "sentinels", in this case characters that are guaranteed
  392.      to make the equality test false, and thus terminate the loop.  */
  393.  
  394.   if (filevec[0].buffered_chars < filevec[1].buffered_chars)
  395.     p0[filevec[0].buffered_chars] = ~p1[filevec[0].buffered_chars];
  396.   else
  397.     p1[filevec[1].buffered_chars] = ~p0[filevec[1].buffered_chars];
  398.  
  399.   /* Loop until first mismatch, or to the sentinel characters.  */
  400.   while (1)
  401.     {
  402.       char c = *p0++;
  403.       if (c != *p1++)
  404.     break;
  405.       if (c == NEWLINE)
  406.     ++lines;
  407.     }
  408.  
  409.   /* Don't count missing newline as part of prefix in RCS mode. */
  410.   if (ROBUST_OUTPUT_STYLE (output_style)
  411.       && ((filevec[0].missing_newline
  412.        && p0 - filevec[0].buffer > filevec[0].buffered_chars)
  413.       ||
  414.       (filevec[1].missing_newline
  415.        && p1 - filevec[1].buffer > filevec[1].buffered_chars)))
  416.     --p0, --p1, --lines;
  417.  
  418.   /* If the sentinel was passed, and lengths are equal, the
  419.      files are identical.  */
  420.   if (p0 - filevec[0].buffer > filevec[0].buffered_chars
  421.       && filevec[0].buffered_chars == filevec[1].buffered_chars)
  422.     {
  423.       filevec[0].prefix_end = p0 - 1 - filevec[0].buffer;
  424.       filevec[1].prefix_end = p1 - 1 - filevec[1].buffer;
  425.       filevec[0].prefix_lines = filevec[1].prefix_lines = lines;
  426.       filevec[0].suffix_begin = 0;
  427.       filevec[1].suffix_begin = 0;
  428.       filevec[0].suffix_lines = filevec[1].suffix_lines = lines;
  429.       return;
  430.     }
  431.  
  432.   /* Point at first nonmatching characters.  */
  433.   --p0, --p1;
  434.  
  435.   /* Skip back to last line-beginning in the prefix.  */
  436.   while (p0 != filevec[0].buffer && p0[-1] != NEWLINE)
  437.     --p0, --p1;
  438.  
  439.   /* Record the prefix.  */
  440.     filevec[0].prefix_end = p0 - filevec[0].buffer;
  441.     filevec[1].prefix_end = p1 - filevec[1].buffer;
  442.     filevec[0].prefix_lines = filevec[1].prefix_lines = lines;
  443.   
  444.   /* Find identical suffix.  */
  445.  
  446.   /* P0 and P1 point beyond the last chars not yet compared.  */
  447.   p0 = filevec[0].buffer + filevec[0].buffered_chars;
  448.   p1 = filevec[1].buffer + filevec[1].buffered_chars;
  449.   lines = 0;
  450.  
  451.   if (! ROBUST_OUTPUT_STYLE (output_style)
  452.       || filevec[0].missing_newline == filevec[1].missing_newline)
  453.     {
  454.       end0 = p0;        /* Addr of last char in file 0.  */
  455.  
  456.       /* Get value of P0 at which we should stop scanning backward:
  457.      this is when either P0 or P1 points just past the last char
  458.      of the identical prefix.  */
  459.       if (filevec[0].buffered_chars < filevec[1].buffered_chars)
  460.     beg0 = filevec[0].prefix_end + filevec[0].buffer;
  461.       else
  462.     /* Figure out where P0 will be when P1 is at the end of the prefix.
  463.        Thus we only need to test P0.  */
  464.     beg0 = (filevec[0].prefix_end + filevec[0].buffer
  465.         + filevec[0].buffered_chars - filevec[1].buffered_chars);
  466.  
  467.       /* Scan back until chars don't match or we reach that point.  */
  468.       while (p0 != beg0)
  469.     {
  470.       char c = *--p0;
  471.       if (c != *--p1)
  472.     {
  473.           /* Point at the first char of the matching suffix.  */
  474.           ++p0, ++p1;
  475.           break;
  476.         }
  477.       if (c == NEWLINE)
  478.     ++lines;
  479.     }
  480.  
  481.       /* Are we at a line-beginning in both files?  */
  482.       if (p0 != end0
  483.       && !((p0 == filevec[0].buffer || p0[-1] == NEWLINE)
  484.           &&
  485.            (p1 == filevec[1].buffer || p1[-1] == NEWLINE)))
  486.     {
  487.       /* No.  We counted one line too many.  */
  488.   --lines;
  489.       /* Advance to next place that is a line-beginning in both files.  */
  490.       do
  491.         {
  492.           ++p0, ++p1;
  493.         }
  494.       while (p0 != end0 && p0[-1] != NEWLINE);
  495.     }
  496.     }
  497.   
  498.   /* Record the suffix.  */
  499.     filevec[0].suffix_begin = p0 - filevec[0].buffer;
  500.     filevec[1].suffix_begin = p1 - filevec[1].buffer;
  501.     filevec[0].suffix_lines = filevec[1].suffix_lines = lines;
  502.   return;
  503. }
  504.  
  505. /* Lines are put into equivalence classes (of lines that match in line_cmp).
  506.    Each equivalence class is represented by one of these structures,
  507.    but only while the classes are being computed.
  508.    Afterward, each class is represented by a number.  */
  509. struct equivclass
  510. {
  511.   struct equivclass *next;    /* Next item in this bucket. */
  512.   struct line_def line;    /* A line that fits this class. */
  513. };
  514.  
  515. /* Hash-table: array of buckets, each being a chain of equivalence classes.  */
  516. static struct equivclass **buckets;
  517.   
  518. /* Size of the bucket array. */
  519. static LONG nbuckets;
  520.  
  521. /* Array in which the equivalence classes are allocated.
  522.    The bucket-chains go through the elements in this array.
  523.    The number of an equivalence class is its index in this array.  */
  524. static struct equivclass *equivs;
  525.  
  526. /* Index of first free element in the array `equivs'.  */
  527. static LONG equivs_index;
  528.  
  529. /* Size allocated to the array `equivs'.  */
  530. static LONG equivs_alloc;
  531.  
  532. /* Largest primes less than some power of two, for nbuckets.  Values range
  533.    from useful to preposterous.  If one of these numbers isn't prime
  534.    after all, don't blame it on me, blame it on primes (6) . . . */
  535. static LONG primes[] =
  536. {
  537.   509,
  538.   1021,
  539.   2039,
  540.   4093,
  541.   8191,
  542.   16381,
  543.   32749,
  544.   65521,
  545.   131071,
  546.   262139,
  547.   524287,
  548.   1048573,
  549.   2097143,
  550.   4194301,
  551.   8388593,
  552.   16777213,
  553.   33554393,
  554.   67108859,            /* Preposterously large . . . */
  555.   -1
  556. };
  557.  
  558. /* Index of current nbuckets in primes. */
  559. static int primes_index;
  560.  
  561. /* Find the equiv class associated with line N of the current file.  */
  562.  
  563. static LONG
  564. find_equiv_class (struct file_data *current, LONG n)
  565. {
  566.   LONG bucket;
  567.   struct equivclass *b, *p = NULL;
  568.  
  569.   /* Equivalence class 0 is permanently allocated to lines that were
  570.      not hashed because they were parts of identical prefixes or
  571.      suffixes. */
  572.   if (n < current->prefix_lines
  573.         || current->linbuf[n].text >= current->suffix_begin + current->buffer)
  574.     return 0;
  575.  
  576.   /* Check through the appropriate bucket to see if there isn't already
  577.      an equivalence class for this line. */
  578.   bucket = current->linbuf[n].hash % nbuckets;
  579.   b = buckets[bucket];
  580.   while (b)
  581.     {
  582.         if (b->line.hash == current->linbuf[n].hash
  583.             && (b->line.length == current->linbuf[n].length
  584.           /* Lines of different lengths can match with certain options.  */
  585.           || length_varies)
  586.             && !line_cmp (&b->line, ¤t->linbuf[n]))
  587.     return b - equivs;
  588.       p = b, b = b->next;
  589.     }
  590.  
  591.   /* Create a new equivalence class in this bucket. */
  592.  
  593.   p = &equivs[equivs_index++];
  594.   p->next = buckets[bucket];
  595.   buckets[bucket] = p;
  596.     p->line = current->linbuf[n];
  597.  
  598.   return equivs_index - 1;
  599. }
  600.  
  601. /* Given a vector of two file_data objects, read the file associated
  602.    with each one, and build the table of equivalence classes.
  603.    Return nonzero if either file appears to be a binary file.  */
  604.  
  605. int
  606. read_files (filevec)
  607.      struct file_data filevec[];
  608. {
  609.     LONG i;
  610.     LONG    j;
  611.   int binary = 0;
  612.   int this_binary = 0;
  613.   int error = 0;
  614.  
  615. #ifndef    EXCLUDE_MAIN
  616.   if ( ! filevec[0].already_read_in)
  617.       binary = this_binary = slurp (&filevec[0]);
  618.   filevec[0].already_read_in = TRUE;
  619.  
  620.   if ( ! filevec[1].already_read_in )
  621.       this_binary = slurp (&filevec[1]);
  622.   filevec[1].already_read_in = TRUE;
  623.   if (binary || this_binary)
  624.     return 1;
  625. #endif    EXCLUDE_MAIN
  626.  
  627.     find_identical_ends (filevec);
  628.  
  629.   for (i = 0; i < 2; ++i)
  630.     {
  631.         error = find_and_hash_each_line (&filevec[i]);
  632.         if ( error )
  633.             goto    done;
  634.     }
  635.  
  636.   /* This is guaranteed to be enough space.  */
  637.     equivs_alloc = filevec[0].buffered_lines + filevec[1].buffered_lines + 1;
  638.   equivs = (struct equivclass *) xmalloc (equivs_alloc * sizeof (struct equivclass));
  639.   if ( ! equivs )
  640.   {
  641.     error = memory_err;
  642.     goto    done;
  643.   }
  644.   /* Equivalence class 0 is permanently safe for lines that were not
  645.      hashed.  Real equivalence classes start at 1. */
  646.   equivs_index = 1;
  647.   
  648.   primes_index = 0;
  649.   while (primes[primes_index] < equivs_alloc / 3)
  650.     primes_index++;
  651.  
  652.   buckets = (struct equivclass **) xmalloc (primes[primes_index] * sizeof (struct equivclass *));
  653.   if ( ! buckets )
  654.   {
  655.     error = memory_err;
  656.     goto done;
  657.   }
  658.   bzero (buckets, primes[primes_index] * sizeof (struct equivclass *));
  659.   nbuckets = primes[primes_index];
  660.  
  661.   for (i = 0; i < 2; ++i)
  662.     {
  663.         struct file_data *current;
  664.         
  665.       current = &filevec[i];
  666.       current->equivs
  667.             = (LONG *) xmalloc (current->buffered_lines * sizeof (*current->equivs));
  668.       if ( ! current->equivs )
  669.       {
  670.     error = memory_err;
  671.     goto done;
  672.       }
  673.    
  674.         for (j = 0; j < current->buffered_lines; ++j)
  675.             current->equivs[j] = find_equiv_class (current, j);
  676.     }
  677.  
  678.   filevec[0].equiv_max = filevec[1].equiv_max = equivs_index;
  679.  
  680. done:
  681.   free (equivs);
  682.   equivs = 0;
  683.   free (buckets);
  684.   buckets = 0;
  685.  
  686.   return error;
  687. }
  688.